﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AppLimit.CloudComputing.SharpBox;
using AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox;
using System.Reflection;
using System.IO;
using InnovTech.Util.Crypto;

namespace InnovTech.Data.Dropbox
{
    public class DropboxSerializer
    {
        private DropboxLoginInfo _loginInfo;
        private StreamCryptor _cryptor;

        private WeakReference _storageRef;

        private bool IsStorageOpened
        {
            get
            {
                if (_storageRef == null || _storageRef.Target == null || !_storageRef.IsAlive)
                    return false;
                return (_storageRef.Target as CloudStorage).IsOpened;
            }
        }

        private CloudStorage Storage
        {
            get
            {
                if (_storageRef == null || !_storageRef.IsAlive)
                    return null;
                return _storageRef.Target as CloudStorage;
            }
            set
            {
                _storageRef = new WeakReference(value);
            }
        }

        public DropboxSerializer(DropboxLoginInfo login, StreamCryptor cryptor)
        {
            _loginInfo = login;
            _cryptor = cryptor;
        }

        private void OpenStorage()
        {
            if (!IsStorageOpened)
            {
                CloudStorage storage = null;
                bool opened = OpenStorage(_loginInfo.AppKey, _loginInfo.AppSecret, _loginInfo.UserName, _loginInfo.Password, out storage);
                if (!opened)
                    throw new Exception("Can not open the connection to the Dropbox server");
                Storage = storage;
            }
        }

        public ICloudFileSystemEntry Serialize(object obj, string targetFileName, string targetDir)
        {

            OpenStorage();

            var appFolder = Storage.GetFolder(targetDir);
            string fileName = Path.GetFileName(targetFileName);

            using (MemoryStream encrypted = _cryptor.Encrypt(obj))
            {
                encrypted.Position = 0;
                ICloudFileSystemEntry file = Storage.UploadFile(encrypted, fileName, appFolder);
                encrypted.Close();
                Storage.Close();

                return file;
            }
            
        }

        public object Deserialize(string targetFileName, string targetDir)
        {
            OpenStorage();

            var appFolder = Storage.GetFolder(targetDir);
            string fileName = Path.GetFileName(targetFileName);
            var appFile = GetFile(fileName, appFolder);
            if (appFile == null)
                throw new ArgumentException("no file with the given name is on the server");

            using (MemoryStream mem = new MemoryStream())
            {
                Storage.DownloadFile(fileName, appFolder, mem);
                mem.Position = 0;
                object decrypted = _cryptor.Decrypt(mem);

                mem.Close();
                Storage.Close();

                return decrypted;
            }
        }


        private ICloudFileSystemEntry GetFile(string file, ICloudDirectoryEntry dir)
        {
            var q = from fe in dir
                    where fe.Name.Equals(file)
                    select fe;
            if (q.Any())
                return q.First();
            return null;
        }

        private bool OpenStorage(string appKey, string appSecret, string userName, string password, out CloudStorage storage)
        {
            ICloudStorageAccessToken accessToken = CreateAccessToken(appKey, appSecret, userName, password);
            return OpenStorage(accessToken, out storage);
        }

        private bool OpenStorage(ICloudStorageAccessToken token, out CloudStorage storage)
        {
            storage = new CloudStorage();
            // get the right config
            ICloudStorageConfiguration cfg = CloudStorage.GetCloudConfigurationEasy(nSupportedCloudConfigurations.DropBox);

            // try to open the box
            storage.Open(cfg, token);
            return storage.IsOpened;
        }

        //with 4 parameters
        internal ICloudStorageAccessToken CreateAccessToken(string appKey, string appSecret, string userName, string password)
        {
            DropBoxBaseTokenInformation info = new DropBoxBaseTokenInformation()
            {
                ConsumerKey = appKey,
                ConsumerSecret = appSecret
            };

            Assembly assembly = Assembly.GetAssembly(typeof(CloudStorage));
            Type type = assembly.GetType("AppLimit.CloudComputing.SharpBox.StorageProvider.DropBox.DropBoxToken");

            ConstructorInfo cInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string), typeof(DropBoxBaseTokenInformation) }, null);
            object dbTokenObj = cInfo.Invoke(new object[] { userName, password, info });
            return dbTokenObj as ICloudStorageAccessToken;
        }


        public void Delete(string targetFileName, string targetDir)
        {
            OpenStorage();
            var appFolder = Storage.GetFolder(targetDir);
            var file = Storage.GetFile(targetFileName, appFolder);
            Storage.DeleteFileSystemEntry(file);
            Storage.Close();
        }

        public void Upload(string localFilePath, string targetDir, string targetFileName)
        {

            

            OpenStorage();

            //var appFolder = cs.GetRoot();
            var appFolder = Storage.GetFolder(targetDir);
            Storage.UploadFile(localFilePath, appFolder);


            //delete
            //var file = cs.GetFile(Path.GetFileName( filePath), appFolder);
            //cs.DeleteFileSystemEntry(file);
            Storage.Close();
        }

        public void Download(string fileName, string parent, string targetPath)
        {
            OpenStorage();
            ICloudDirectoryEntry dir = Storage.GetFolder(parent);
            Storage.DownloadFile(dir, fileName, targetPath);
            Storage.Close();
        }
    }
}
